/*
* File: handleTelnet.c
* Version: 1.0
* Last modified on Fri Mars 07 2014 by group 5
* Members:
* Johan Forsblom, Johanna Carlsson, Dragana Todorovic, 
* Hampus Herbertsson, Robin Henriksson
* -----------------------------------------------------
* This file implements the handleTelnet.h interface.
*/

#include <winsock2.h> 
#include <Ws2tcpip.h>
#include <stdio.h>
#include <process.h>
#include "http_parser.h"
#include "handleTelnet.h"
#include "webserver.h"
#include <Windows.h>
#include "configFile.h"


/* Constants */
#define TELNET_BUFFER 512
#define DEFAULT_SEND_BUFF 140
#define PASSWORD "PASSWORD"
#define LINE_LENGTH 100

/* Constants for keycodes */
#define NEW_LINE 0x0a 
#define DELETE_KEY 0x7f
#define BACKSPACE_KEY 0x08
#define CARRIAGE_RETURN 0x0d
#define DATA_LINK_ESCAPE 0x10


/* This is a struct for the telnet buffer*/
typedef struct{
	char buffer[TELNET_BUFFER];
	int counter;
} telnetBufT;


/* static global variables */
static int closeConnection = 0;
static int loggedIn = 0;

/* Functions prototypes */
void initTelnetBuf(telnetBufT *buf);
void convertToUpperCase(char *string);
void sendStartMenu(SOCKET *clientSocket);
void sendClearScreen(SOCKET *clientSocket);
void sendLoggedInMenu(SOCKET *clientSocket);
void sendMessage(SOCKET *clientSocket, char *string);
int checkLogOut(SOCKET *clientSocket, char *command);
int checkIsActive(SOCKET *clientSocket, char *command);
int checkGetLogFile(SOCKET *clientSocket, char *command);
int readTelnetInput(SOCKET *clientSocket, telnetBufT *buf);
void runTelnetCommand(SOCKET *clientSocket, telnetBufT * buf);
int checkAuthorize(SOCKET *clientSocket, char *command, char *password);
int checkServerStartStop(SOCKET *clientSocket, char *command, char *arg);


/* Function: startTelnetThread
* usage: startTelnetThread(NULL);
* ----------------------------------------------------------------
* This function starts the telnetThread.
*/
void startTelnetThread(void *arg)
{
	struct sockaddr clientAddr;
	struct addrinfo *info;
	int clientAddrLen, ok;
	int done=0;
	char telNetPort[10];
	telnetBufT buf;

	SOCKET clientSocket;
	SOCKET telnetSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
	getTelNetPort(telNetPort);

	initTelnetBuf(&buf);

	if (telnetSocket < 0){
		printf("ERROR: server socket failed\n");
		system("pause");
		exit(1);
	}

	ok = getaddrinfo("localhost", telNetPort, NULL, &info);

	if(ok !=0) {
		WCHAR * error = gai_strerror(ok);
		printf("%s\n",error);
		system("pause");
	}
	else{ 
		while(info->ai_family != AF_INET && info->ai_next != NULL)
			info = info->ai_next;
	}

	ok = bind(telnetSocket, info->ai_addr, info->ai_addrlen);
	if(ok == SOCKET_ERROR) {
		int err = WSAGetLastError();
		printf("%d\n",err);
		system("pause");
		exit(1);
	}
	ok = listen(telnetSocket, SOMAXCONN );
	if(ok == SOCKET_ERROR) {
		int err = WSAGetLastError();
		printf("%d\n",err);
		system("pause");
		exit(1);
	}

	while(!done){		
		clientAddrLen = sizeof(clientAddr); 
		clientSocket = accept(telnetSocket, &clientAddr, &clientAddrLen); 

		//check if the socket is valid
		if(clientSocket != INVALID_SOCKET && telnetSocket!= SOCKET_ERROR) { 
			char hostName[100]; 
			char portName[100]; 

			//get info about the connecting client
			int ok = getnameinfo(&clientAddr, clientAddrLen, hostName, 100, portName, 100, NI_NUMERICSERV ); 

			//print out incoming connection
			if(ok == 0) {
				printf("Accept Telnet incoming from: %s at port %s\n",hostName,portName);		
				sendClearScreen(&clientSocket);
				sendStartMenu(&clientSocket);
			}
		} 
		else {
			printf("Could not create socket!\n");
			system("pause");
			exit(1);
		}
		if(readTelnetInput(&clientSocket, &buf))
			closesocket(clientSocket);
	}
	_endthread();
}


/* Function: initTelnetBuf
* usage: initTelnetBuf(&buf);
* ----------------------------------------------------------------
* This function initializes a telnetBufT struct.
*/
void initTelnetBuf(telnetBufT *buf)
{
	buf->counter = 0;
	memset(buf->buffer, 0, TELNET_BUFFER);
}


/* Function: readTelnetInput
* usage: readTelnetInput(&clientSocket, &buf);
* ----------------------------------------------------------------
* This function reads the input from telnet and calls function runTelnetCommand
* when a new line character comes from telnet. Returns 1 if connection was closed.
*/
int readTelnetInput(SOCKET *clientSocket, telnetBufT *buf)
{
	int ok;
	char c;
	int checkEnd = 0;
	while(TRUE){
		ok = recv(*clientSocket, &c, 1, 0);

		if(c == BACKSPACE_KEY){			  
			if(buf->counter > 0){
				buf->counter--;
				buf->buffer[buf->counter] = '\0';				
			}
		}
		else if(c == CARRIAGE_RETURN){	
			if(strlen(buf->buffer) > 0){
				buf->buffer[buf->counter] = '\0';
				runTelnetCommand(clientSocket, buf);
				buf->counter = 0;
				memset(buf->buffer, 0, TELNET_BUFFER);	

				if(loggedIn)
					sendLoggedInMenu(clientSocket);
				else if(!closeConnection)
					sendStartMenu(clientSocket);

				memset(buf->buffer, 0, TELNET_BUFFER);				
			}
			checkEnd = 0;
		}
		else if(c == NEW_LINE)
			checkEnd++;
		else if(c == DATA_LINK_ESCAPE){}
		else if(c == DELETE_KEY){}
		else{
			buf->buffer[buf->counter] = c;
			if(buf->counter < TELNET_BUFFER)
				buf->counter++;
			checkEnd = 0;
		}

		if(closeConnection || checkEnd > 2){
			closeConnection = 0;
			return 1;
		}
	}
	return 0;
}


/* Function: runTelnetCommand
* usage: runTelnetCommand(&clientSocket, &buf);
* ----------------------------------------------------------------
* This function parses the command from the telnet client. Then it
* executes the given command if it's valid. Otherwise ask the user to
* enter a new command. 
*/
void runTelnetCommand(SOCKET *clientSocket, telnetBufT *buf)
{	
	char commandText[20];
	char commandArg[20];

	sscanf(buf->buffer, "%s %s", commandText, commandArg);

	convertToUpperCase(commandText);
	convertToUpperCase(commandArg);

	if(checkAuthorize(clientSocket, commandText, commandArg))
		return;

	else if(loggedIn){		
		if(checkServerStartStop(clientSocket, commandText, commandArg)) 
			return;
		else if(checkIsActive(clientSocket, commandText))
			return;
		else if (checkGetLogFile(clientSocket, commandText))
			return;		
		else if(checkLogOut(clientSocket, commandText))
			return;	
	}
	sendClearScreen(clientSocket);
	sendMessage(clientSocket, "Invalid command\n\r");
	return;
}


/* Function: checkAuthorize
* usage: checkAuthorize(clientSocket, command, password);
* ----------------------------------------------------------------
* This function validates login from the client. 
*/
int checkAuthorize(SOCKET *clientSocket, char *command, char *password)
{
	if(strcmp(command, "AUTHORIZE") == 0 && loggedIn ==0){
		if(strcmp(password, PASSWORD) == 0){
			loggedIn = 1;
			sendClearScreen(clientSocket);
			sendMessage(clientSocket, "Logged In. \n\r");
			printf("Telnet user logged in\n");
		}
		else{
			sendClearScreen(clientSocket);
			sendMessage(clientSocket, "\n\rWrong password.\n\r\n\r");
		}
		return 1;
	}	
	return 0;
}


/* Function: checkServerStartStop
* usage: checkServerStartStop(clientSocket, command, arg);
* ----------------------------------------------------------------
* This function starts or stops the server.
*/
int checkServerStartStop(SOCKET *clientSocket, char *command, char *arg)
{
	if(strcmp(command, "SERVER") == 0){
		if(strcmp(arg, "START") == 0){				
			if(getServerStatus()){
				sendClearScreen(clientSocket);
				sendMessage(clientSocket, "Server is already responding to incoming requests!\n\r");
			}
			else{
				SOCKET *socket = getServerSocket();
				startServerSocket(socket);
				setServerStatus(SERVER_ACTIVE);
				Sleep(2500); /* Sleep for 2,5 sec so server thread will change */
				sendClearScreen(clientSocket);
				sendMessage(clientSocket, "Server is responding to incoming requests!\n\r");
				printf("Server responding to incoming requests! \n");
			}
		}
		else if(strcmp(arg, "STOP") == 0){
			if(!getServerStatus()){
				sendClearScreen(clientSocket);
				sendMessage(clientSocket, "Server is already ignoring incoming requests!\n\r");					
			}
			else{
				SOCKET *socket = getServerSocket();
				closeServerSocket(socket);
				setServerStatus(SERVER_INACTIVE);
				Sleep(2500); /* Sleep for 2,5 sec so server thread will change */
				sendClearScreen(clientSocket);
				sendMessage(clientSocket, "Server is ignoring incoming requests!\n\r");
				printf("Server is ignoring incoming requests! \n");
			}
		} 
		else {
			sendMessage(clientSocket, "Invalid command\n\r");					
		}
		return 1;
	}
	return 0;
}


/* Function: checkIsActive
* usage: checkIsActive(clientSocket, command);
* ----------------------------------------------------------------
* This function tells the client if the server is active or not.
*/
int checkIsActive(SOCKET *clientSocket, char *command)
{
	int status = 0;

	if(strcmp(command, "ISACTIVE") == 0){			
		status = getServerStatus();	

		if(status == SERVER_ACTIVE){
			sendClearScreen(clientSocket);
			sendMessage(clientSocket, "Responding to incoming requests.\n\r");
		} 
		else if(status == SERVER_INACTIVE){
			sendClearScreen(clientSocket);
			sendMessage(clientSocket, "Ignoring incoming requests.\n\r");
		}
		return 1;
	}
	return 0;
}


/* Function: checkGetLogFile
* usage: checkGetLogFile(clientSocket, command);
* ----------------------------------------------------------------
* This function sends the current logfile to the client.
*/
int checkGetLogFile(SOCKET *clientSocket, char *command)
{
	FILE *file;
	char buf[100];

	if(strcmp(command, "GETLOGFILE") == 0){
		file = fopen("serverLogFile.txt", "a+");
		if(file == NULL){
			printf("Error could not open serverLogFile.txt for writing\n");
		} 
		else {
			sendMessage(clientSocket, "\n\rLogfile contains:\n\r\n\r" );			
			while(fgets(buf,LINE_LENGTH,file)!=NULL){
				sendMessage(clientSocket, buf);
				sendMessage(clientSocket, "\r");
			}			
			fclose(file);
			return 1;	
		}			
	}
	return 0;
}


/* Function: checkLogOut
* usage: checkLogOut(clientSocket, command);
* ----------------------------------------------------------------
* This function logout the client.
*/
int checkLogOut(SOCKET *clientSocket, char *command)
{
	if(strcmp(command, "LOGOUT") == 0){			
		loggedIn = 0;
		printf("Telnet client logged out \n");
		sendClearScreen(clientSocket);
		sendMessage(clientSocket, "Logged Out\n\r\n\r");
		Sleep(2000); /* Sleep for two second so the client gets the logout message */
		closeConnection = 1;
		return 1;
	}
	return 0;
}


/* Function: sendStartMenu
* usage: sendStartMenu(&clientSocket);
* ----------------------------------------------------------------
* This function sends the welcome menu to the telnet client. 
*/
void sendStartMenu(SOCKET *clientSocket)
{	
	sendMessage(clientSocket, "Welcome!\n\r\n\rType: Authorize <PASSWORD> to log in.\n\r-> ");
}


/* Function: sendStartMenu
* usage: sendStartMenu(&clientSocket);
* ----------------------------------------------------------------
* This function sends the logged in menu to the telnet client. 
*/
void sendLoggedInMenu(SOCKET *clientSocket)
{
	sendMessage(clientSocket, "\n\r\n\rValid Commands\n\r\n\r1.SERVER <START or STOP> \n\r2.ISACTIVE \n\r3.GETLOGFILE \n\r4.LOGOUT\n\r\n\rEnter command: ");
}


/* Function: sendMessage
* usage: sendMessage(clientSocket, string);
* ----------------------------------------------------------------
* This function sends the given message to the telnet client.
*/
void sendMessage(SOCKET *clientSocket, char *string)
{
	send(*clientSocket, string, strlen(string), 0);
}


/* Function: convertToUpperCase
* usage: convertToUpperCase(string);
* ----------------------------------------------------------------
* This function converts a string to uppercase. 
*/
void convertToUpperCase(char *string)
{
	char c;
	int i=0;
	while(string[i]) {
		c = toupper(string[i]);
		string[i] = c;
		i++;
	}
}


/* Function: sendClearScreen
* usage: sendClearScreen(clinetSocket);
* ----------------------------------------------------------------
* This function clears the telnet screen. 
*/
void sendClearScreen(SOCKET *clientSocket)
{
	sendMessage(clientSocket, "\u001B[2J");
}
